// 基于IndexDB封装的仿localStage用法的工具,用法：
/*this.dbStorage = DBStorage.getInstance({
     busiType: 'aiChat'
})*/

import {VERSION_CONFIG} from './indexDbConfig.js';
// 时间戳
const TIME_STAMP = 'timeStamp';
//库名
const DB_NAME = 'mydatabaseweb';

// 新建或者打开数据库的过程都是异步的，我们需要等待数据库打开后，拿到实例
function promisify(request) {
    return new Promise((resolve, reject) => {
        request.onsuccess = () => resolve(request.result);
        request.onerror = () => reject(request.error);
    });
}

/**
 * @bName:数据库名
 * @storeName:表名/仓库名
 * @version:版本
 * @keyPath:主键
 */

class DBStorage {

    constructor({ busiType, keyPath = "id"}) {
        this.version = VERSION_CONFIG.version;
        let storeList = VERSION_CONFIG.storeList;
        let storeName = '';
        for (let item of storeList){
            if(item[busiType]){
                storeName = item[busiType];
                break;
            }
        }
        if(!storeName){
            throw Error('请在indexDbConfig中配置表名')
        }
        //1.有就打开，无就创建
        const request = window.indexedDB.open(DB_NAME, this.version);
        // 2.onupgradeneeded数据库创建或升级的时候会触发，新建表
        request.onupgradeneeded = function (event) {
            console.log('升级成功');
            let db = event.target.result;
            if (!db.objectStoreNames.contains(storeName)) {
                let city_os = db.createObjectStore(storeName, {keyPath: keyPath});
                // IDBObjectStore.createIndex(name, keyPath, [parameters])
                //创建时间索引
                city_os.createIndex(TIME_STAMP, TIME_STAMP, {unique: false});
            }
        };

        this.dbPromise = promisify(request);
        this.storeName = storeName;
    }

    static getInstance(dbOptions) {
        let indexedDB = window.indexedDB;
        if (!indexedDB) {
            throw Error('你的客户端不支持indexedDB')
        }
        // 单例
        if (DBStorage._instance) {
            return DBStorage._instance;
        } else {
            DBStorage._instance = new DBStorage(dbOptions);
        }
        return DBStorage._instance;
    }

    // 事务是 IndexedDB 中非常重要的概念，它用于确保对数据库的操作是原子性、一致性、隔离性和持久性的。
    async getStore(operationMode, storeName = this.storeName) {
        const db = await this.dbPromise;
        // transaction事务 指定操作模式（"只读"或"读写"）
        return db.transaction(storeName, operationMode).objectStore(storeName);// 仓库对象
    }

    // 设置
    async setItem(data) {
        if(data?.constructor === Object ){
            let CurrentTime =new Date().getTime();//时间戳
            let addTimeData = Object.assign(data,{[TIME_STAMP]:CurrentTime})
            const store = await this.getStore('readwrite');
            return promisify(store.put(addTimeData));
        }else{
            throw Error('设置表数据的类型为object类型')
        }
    }

    //添加
    async addItem(data) {
        if(data?.constructor === Object ){
            let CurrentTime =new Date().getTime();//时间戳
            // let addTimeData = Object.assign(data,{[TIME_STAMP]:CurrentTime})
            data[TIME_STAMP] = CurrentTime
            const store = await this.getStore('readwrite');
            return promisify(store.put(data));
        }else{
            throw Error('添加表数据的类型为object类型')
        }
    }

    //获取
    async getItem(key) {
        const store = await this.getStore('readonly');
        return promisify(store.get(key));
    }

    // 根据keyPath主键批量查询
    /**
     *
     * @param range 范围 IDBKeyRange.lowerBound(x) IDBKeyRange.upperBound(y) IDBKeyRange.bound(x, y)
     * @param direction 方向排序
     * @param primarykeyFlag true根据主键id排序，false根据索引timeStamp
     * @returns {Promise<unknown>}
     * 不填查所有
     */

    async getAll(range = null,direction = "next") {
        const store = await this.getStore('readwrite');
        return new Promise((resolve, reject) => {
            let CursorRuqest = store.openCursor(range,direction);
            let result = [];
            CursorRuqest.onsuccess = function () {
                let cursor = CursorRuqest.result;
                if (cursor) {
                    result.push(cursor.value);
                    cursor.continue();
                } else {
                    resolve(result);
                }
            };
            CursorRuqest.onerror = () => reject(CursorRuqest.error);
        });

    }

    //删除
    async removeItem(key) {
        const store = await this.getStore('readwrite');
        return promisify(store.delete(key));
    }

    //清除
    async clear() {
        const store = await this.getStore('readwrite');
        return promisify(store.clear());
    }

    /**
     *  按时间删除，time之前的时间全部删除
     * @param time 时间戳-毫秒
     * @returns {Promise<unknown>}
     */
    async deleteByTime(time){
        const store = await this.getStore('readwrite');
        let keyRange = IDBKeyRange.upperBound(time)
        return new Promise((resolve, reject) => {
            let CursorRuqest = store.index(TIME_STAMP).openCursor(keyRange);
            CursorRuqest.onsuccess = (event) => {
                const cursor = CursorRuqest.result;
                console.log(cursor);
                if(cursor){
                    cursor.delete();
                    cursor.continue();
                }
            };
            CursorRuqest.onerror = () => reject(CursorRuqest.error);
        })
    }

    /**
     * 分页查询根据时间索引查询
     * @param page number页码
     * @param pageSize number页数
     * @returns {Promise<unknown>}
     */
    async getPageList(page,pageSize){
        let counter = 0; // 计数器
        let list = [];
        let advanced = true;
        const store = await this.getStore('readonly');
        return new Promise((resolve, reject)=>{
            const myIndex = store.index(TIME_STAMP);
            myIndex.openCursor().onsuccess = (event) => {
                let cursor = event.target.result;
                if (page > 1&&cursor&&advanced) {
                    advanced = false
                    cursor.advance((page - 1) * pageSize); // 跳过多少条
                    return;
                }
                if (cursor) {
                    // 必须要检查
                    list.push(cursor.value);
                    counter++;
                    if (counter < pageSize) {
                        cursor.continue(); // 遍历了存储对象中的所有内容
                    } else {
                        cursor = null;
                    }
                } else {
                    resolve(list)
                }
            };
            myIndex.openCursor().onerror = (e) => reject(event.target.error);
        })
    }
}

export default DBStorage;

